package org.rslai.socket.testtool.isocket;

import java.io.*;
import java.net.*;

/**
 * DotNet 创建连接并且处理接收、发送数据
 * @author rslai
 *
 */
class ISocketDotNet extends ISocket {
	private InputStream readBoundByte; // 得到字节输入流，按 byte（8位） 读入

	/**
	 * 根据输入的参数建立一个Socket连接
	 * @param server 服务器IP或域名
	 * @param port 服务端口
	 * @param charset 传送信息的字符集
	 * @throws IOException
	 */
	public ISocketDotNet(String server, int port, String charset) throws IOException {
		super(charset);

		try {
			// 创建指定 Socket 链接
			clientSocket = new Socket(server, port);
			logger.info("clientSocket: " + clientSocket);

			writeBound = new DataOutputStream(clientSocket.getOutputStream()); // 得到输出流
			readBoundByte = (clientSocket.getInputStream()); // 得到字节输入流，按 byte（8位） 读入
		} catch (UnknownHostException uhe) {
			logger.info("UnknownHostException: " + uhe);
		} catch (IOException ioe) {
			System.err.println("IOException: " + ioe);
			throw ioe;
		}
	}

	/**
	 * 关闭打开 Socket
	 */
	public void close() throws Exception {
		writeBound.close();
		readBoundByte.close();
		clientSocket.close();
	}

	/**
	 * 从服务器接收信息
	 * @return 返回接收到的数据
	 * @throws Exception
	 */
	public ReceiveData receive() throws Exception{
		ReceiveData receiveData = new ReceiveDataDotNet();

		receiveData.setHead(receiveHead()); // 取得服务器传回的数据头信息
		receiveData.setBody(receiveBody(receiveData.getBodyLength())); // 取得服务器传回的数据体信息

		logger.debug("receive : \r\n" + receiveData.toString());

		return receiveData;
	}

	/**
	 * 读取头信息
	 * @return 读到的头信息
	 * @throws Exception
	 */
	private byte[] receiveHead() throws Exception {
		int lenght = 16; // 接收的头信息长度
		byte[] receiveHead = new byte[lenght]; // 保存收到的头信息

		while (true) {
			int begin = 0; // 保存已收到字节的总数
			byte[] tmp =  new byte[lenght];

			// 读取指定长度的byte
			int len = readBoundByte.read(tmp);
			if (len > 0) {
				lenght = lenght - len;

				System.arraycopy(tmp, 0, receiveHead, begin, len);
				begin = len;
			}

			// 判断读完后退出接收
			if (lenght <= 0) {
				break;
			}
		}

		return receiveHead;
	}

	/**
	 * 按指定长度读取体信息
	 * @param lenght 体信息的长度
	 * @return 读到的体信息
	 * @throws Exception
	 */
	protected StringBuffer receiveBody(int lenght) throws Exception {
		StringBuffer bodyStrBuf = new StringBuffer(); // 保存读到的数据

		while (true) {
			// 设置本次读入长度
			byte[] tmp;
			if (lenght > bufSize) {
				tmp = new byte[bufSize];
			} else {
				tmp = new byte[lenght];
			}

			// 读取指定长度的byte
			int len = readBoundByte.read(tmp);
			if (len > 0) {
				lenght = lenght - len;
				byte [] getByte = new byte[len];

				System.arraycopy(tmp, 0, getByte, 0, len);
				bodyStrBuf.append(new String(getByte, charset)); // 转为字符添加到输出数据中
			}

			// 判断读完后退出接收
			if (lenght <= 0) {
				break;
			}
		}

		/* 之前使用的代码，问题有2
		 * 1、一次读入一个字符效率有问题
		 * 2、不能以正常字符显示接收到的信息。
		for (int i=0; i<lenght; i++) {
			bodyStrBuf.append((char)readBoundByte.read());
		}
		*/

		return bodyStrBuf;
	}

}